// ======================================================================
// \title  Health.hpp
// \author jdperez
// \brief  cpp file for Health test harness implementation class
//
// \copyright
// Copyright 2009-2015, by the California Institute of Technology.
// ALL RIGHTS RESERVED.  United States Government Sponsorship
// acknowledged.
//
// ======================================================================

#include "HealthTester.hpp"
#include <Fw/Test/UnitTest.hpp>

#define INSTANCE 0
#define MAX_HISTORY_SIZE                                                                                         \
    (Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * \
     Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS)
#define QUEUE_DEPTH (Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 2)
#define FLAG_KEY_VALUE 0xcafecafe

static_assert(Svc::HealthTester::getNumPingSendOutputPorts() < 0xcafecafe, "");

namespace Svc {

// ----------------------------------------------------------------------
// Construction and destruction
// ----------------------------------------------------------------------

HealthTester ::HealthTester() : HealthGTestBase("Tester", MAX_HISTORY_SIZE), component("Health") {
    this->connectPorts();
    this->initComponents();
}

HealthTester ::~HealthTester() {
    this->component.deinit();
}

// ----------------------------------------------------------------------
// Handlers for typed from ports
// ----------------------------------------------------------------------

void HealthTester ::from_PingSend_handler(const FwIndexType portNum, U32 key) {
    if (this->override) {
        invoke_to_PingReturn(portNum, this->override_key);
    } else {
        ASSERT_TRUE(portNum < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);

        if (key == this->keys[portNum]) {
            this->invoke_to_PingReturn(portNum, key);
        }
    }

    this->pushFromPortEntry_PingSend(key);
}

void HealthTester ::from_WdogStroke_handler(const FwIndexType portNum, U32 code) {
    this->pushFromPortEntry_WdogStroke(code);
}

// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------

void HealthTester ::connectPorts() {
    // PingReturn
    for (FwIndexType i = 0; i < this->component.getNum_PingReturn_InputPorts(); ++i) {
        this->connect_to_PingReturn(i, this->component.get_PingReturn_InputPort(i));
    }

    // Run
    this->connect_to_Run(0, this->component.get_Run_InputPort(0));

    // CmdDisp
    this->connect_to_CmdDisp(0, this->component.get_CmdDisp_InputPort(0));

    // PingSend
    for (FwIndexType i = 0; i < this->component.getNum_PingSend_OutputPorts(); ++i) {
        this->component.set_PingSend_OutputPort(i, this->get_from_PingSend(i));
    }

    // WdogStroke
    this->component.set_WdogStroke_OutputPort(0, this->get_from_WdogStroke(0));

    // CmdStatus
    this->component.set_CmdStatus_OutputPort(0, this->get_from_CmdStatus(0));

    // CmdReg
    this->component.set_CmdReg_OutputPort(0, this->get_from_CmdReg(0));

    // Tlm
    this->component.set_Tlm_OutputPort(0, this->get_from_Tlm(0));

    // Time
    this->component.set_Time_OutputPort(0, this->get_from_Time(0));

    // Log
    this->component.set_Log_OutputPort(0, this->get_from_Log(0));

    // LogText
    this->component.set_LogText_OutputPort(0, this->get_from_LogText(0));
}

void HealthTester ::initComponents() {
    this->init();

    this->numPingEntries = Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS;
    this->watchDogCode = 42;

    this->component.init(QUEUE_DEPTH, INSTANCE);

    for (FwIndexType entry = 0; entry < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; entry++) {
        this->pingEntries[entry].warnCycles = Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS + entry;
        this->pingEntries[entry].fatalCycles = Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 2 + entry + 1;
        this->pingEntries[entry].entryName.format("task%d", entry);
    }

    this->component.setPingEntries(this->pingEntries, this->numPingEntries, this->watchDogCode);
    this->override = false;
    this->override_key = 0;

    for (FwIndexType i = 0; i < this->numPingEntries; i++) {
        this->keys[i] = 100000;
    }
}

void HealthTester ::dispatchAll() {
    HealthComponentBase::MsgDispatchStatus stat = HealthComponentBase::MSG_DISPATCH_OK;
    while (HealthComponentBase::MSG_DISPATCH_OK == stat) {
        stat = this->component.doDispatch();
        FW_ASSERT(stat != HealthComponentBase::MSG_DISPATCH_ERROR);
    }
}

// ----------------------------------------------------------------------
// Tests
// ----------------------------------------------------------------------

void HealthTester ::nominalTlm() {
    TEST_CASE(900.1.1, "Nominal Telemetry");
    REQUIREMENT("ISF-HTH-001");
    COMMENT("The Svc::Health component shall ping each output port specified in the provided table.");

    // Check initial state is as expected
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);
    ASSERT_CMD_RESPONSE_SIZE(0);

    for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; port++) {
        this->keys[port] = port;
    }

    // invoke run handler same number as number of ports
    for (U32 i = 0; i < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; i++) {
        this->invoke_to_Run(0, 0);
        for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; port++) {
            this->keys[port] += Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS;
        }

        // Check no events or telemetry have occurred
        ASSERT_EVENTS_SIZE(0);
        ASSERT_TLM_SIZE(0);
        ASSERT_CMD_RESPONSE_SIZE(0);
    }

    // Check no events or telemetry have occurred
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);
    ASSERT_CMD_RESPONSE_SIZE(0);
}

void HealthTester ::warningTlm() {
    TEST_CASE(900.1.2, "Warning Telemetry");
    REQUIREMENT("ISF-HTH-002");
    COMMENT("The Svc::Health component shall track the timeout cycles for each component.");
    // Check initial state is as expected
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);
    ASSERT_CMD_RESPONSE_SIZE(0);
    return;

    // invoke run handler enough times for warnings
    for (U32 i = 0; i < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 2; i++) {
        this->invoke_to_Run(0, 0);
        for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; port++) {
            ASSERT_EQ(i + 1, this->component.m_pingTrackerEntries[port].cycleCount);
        }
    }

    // check for expected warning EVRs from each entry
    ASSERT_EVENTS_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    ASSERT_TLM_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    ASSERT_EVENTS_HLTH_PING_WARN_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);

    for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; port++) {
        char name[80];
        snprintf(name, sizeof(name), "task%d", port);
        ASSERT_EVENTS_HLTH_PING_WARN(port, name);
    }
}

void HealthTester ::faultTlm() {
    TEST_CASE(900.1.3, "Fault Telemetry");
    REQUIREMENT("ISF-HTH-003");
    COMMENT(
        "The Svc::Health component shall issue a FATAL event if a component fails to return a ping by the specified "
        "timeout.");

    // Check initial state is as expected
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);
    ASSERT_CMD_RESPONSE_SIZE(0);

    // invoke run handler enough times for warnings
    for (U32 i = 0; i < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 2; i++) {
        this->invoke_to_Run(0, 0);
        for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; port++) {
            ASSERT_EQ(i + 1, this->component.m_pingTrackerEntries[port].cycleCount);
        }
    }

    // check for expected warning EVRs from each entry
    ASSERT_EVENTS_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    ASSERT_TLM_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    ASSERT_EVENTS_HLTH_PING_WARN_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);

    for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; port++) {
        char name[80];
        snprintf(name, sizeof(name), "task%d", port);
        ASSERT_EVENTS_HLTH_PING_WARN(port, name);
    }

    this->clearEvents();
    this->clearTlm();

    // invoke run handler enough times for FATALs
    for (U32 i = 0; i < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; i++) {
        this->invoke_to_Run(0, 0);
        for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; port++) {
            ASSERT_EQ(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 2 + i + 1,
                      this->component.m_pingTrackerEntries[port].cycleCount);
        }
    }
    this->invoke_to_Run(0, 0);

    // check for expected warning EVRs from each entry
    ASSERT_EVENTS_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    ASSERT_EVENTS_HLTH_PING_LATE_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);

    for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; port++) {
        char name[80];
        snprintf(name, sizeof(name), "task%d", port);
        ASSERT_EVENTS_HLTH_PING_LATE(port, name);
    }
}

void HealthTester ::disableAllMonitoring() {
    TEST_CASE(900.1.4, "Enable/Disable all monitoring");
    REQUIREMENT("ISF-HTH-004");
    COMMENT("The Svc::Health component shall have a command to enable or disable all monitoring.");

    // Check initial state is as expected
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);
    ASSERT_CMD_RESPONSE_SIZE(0);

    // invoke run handler
    for (U32 i = 0; i < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 2; i++) {
        this->invoke_to_Run(0, 0);
        for (FwIndexType entry = 0; entry < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; entry++) {
            ASSERT_EQ(i + 1, this->component.m_pingTrackerEntries[entry].cycleCount);
        }
    }

    ASSERT_EVENTS_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    ASSERT_EVENTS_HLTH_PING_WARN_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    for (FwIndexType entry = 0; entry < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; entry++) {
        char name[80];
        snprintf(name, sizeof(name), "task%d", entry);
        ASSERT_EVENTS_HLTH_PING_WARN(entry, name);
    }

    ASSERT_TLM_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);

    this->clearEvents();
    this->clearTlm();

    // disable all monitoring
    this->sendCmd_HLTH_ENABLE(0, 0, Fw::Enabled::DISABLED);
    this->dispatchAll();

    ASSERT_EVENTS_SIZE(1);
    ASSERT_EVENTS_HLTH_CHECK_ENABLE_SIZE(1);
    ASSERT_EVENTS_HLTH_CHECK_ENABLE(0, Fw::Enabled::DISABLED);

    ASSERT_EQ(this->component.m_enabled, Fw::Enabled::DISABLED);

    // invoke run handler 100 times
    for (U32 i = 0; i < 100; i++) {
        this->invoke_to_Run(0, 0);
        // cycle count should stay the same

        ASSERT_EQ(static_cast<U32>(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS) * 2,
                  this->component.m_pingTrackerEntries[0].cycleCount);
    }

    // confirm no telemetry was received
    ASSERT_TLM_SIZE(0);

    this->clearEvents();
    this->clearTlm();

    // enable all monitoring
    this->sendCmd_HLTH_ENABLE(0, 0, Fw::Enabled::ENABLED);
    this->dispatchAll();

    ASSERT_EVENTS_SIZE(1);
    ASSERT_EVENTS_HLTH_CHECK_ENABLE_SIZE(1);
    ASSERT_EVENTS_HLTH_CHECK_ENABLE(0, Fw::Enabled::ENABLED);

    ASSERT_EQ(this->component.m_enabled, Fw::Enabled::ENABLED);

    this->clearEvents();
    this->clearTlm();
    // check that cycle counts increase again
    for (U32 i = 0; i < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; i++) {
        this->invoke_to_Run(0, 0);
        for (FwIndexType entry = 0; entry < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; entry++) {
            ASSERT_EQ(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 2 + 1 + i,
                      this->component.m_pingTrackerEntries[entry].cycleCount);
        }
    }
    this->invoke_to_Run(0, 0);

    // Should be FATAL timeouts
    ASSERT_EVENTS_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    ASSERT_EVENTS_HLTH_PING_LATE_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    for (FwIndexType entry = 0; entry < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; entry++) {
        char name[80];
        snprintf(name, sizeof(name), "task%d", entry);
        ASSERT_EVENTS_HLTH_PING_LATE(entry, name);
    }
}

void HealthTester ::disableOneMonitoring() {
    TEST_CASE(900.1.5, "Enable/Disable individual monitors");
    REQUIREMENT("ISF-HTH-005");
    COMMENT("The Svc::Health component shall have a command to enable or disable monitoring for a particular port.");

    // Check initial state is as expected
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);
    ASSERT_CMD_RESPONSE_SIZE(0);

    // disable monitoring one at a time

    for (FwIndexType entry = 0; entry < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; entry++) {
        this->clearEvents();
        this->clearHistory();
        // reset cycle count
        for (FwIndexType e2 = 0; e2 < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; e2++) {
            this->component.m_pingTrackerEntries[e2].cycleCount = 0;
        }
        // disable entry
        char name[80];
        snprintf(name, sizeof(name), "task%d", entry);
        Fw::CmdStringArg task(name);
        this->sendCmd_HLTH_PING_ENABLE(0, 10, task, Fw::Enabled::DISABLED);
        this->dispatchAll();
        ASSERT_CMD_RESPONSE_SIZE(1);
        ASSERT_CMD_RESPONSE(0, HealthComponentBase::OPCODE_HLTH_PING_ENABLE, 10, Fw::CmdResponse::OK);

        ASSERT_EVENTS_SIZE(1);
        ASSERT_EVENTS_HLTH_CHECK_PING(0, Fw::Enabled::DISABLED, name);

        // run a few cycles
        for (FwIndexType cycle = 0; cycle < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; cycle++) {
            // run a cycle
            this->invoke_to_Run(0, 0);

            // verify only enable entries count up
            for (FwIndexType e3 = 0; e3 < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; e3++) {
                if (e3 == entry) {
                    // shouldn't be counting up
                    ASSERT_EQ(0u, this->component.m_pingTrackerEntries[e3].cycleCount);
                } else {
                    // others should be counting up
                    ASSERT_EQ(static_cast<U32>(cycle + 1), this->component.m_pingTrackerEntries[e3].cycleCount);
                }
            }
        }

        this->clearEvents();
        this->clearHistory();

        this->sendCmd_HLTH_PING_ENABLE(0, 10, task, Fw::Enabled::ENABLED);
        this->dispatchAll();
        ASSERT_CMD_RESPONSE_SIZE(1);
        ASSERT_CMD_RESPONSE(0, HealthComponentBase::OPCODE_HLTH_PING_ENABLE, 10, Fw::CmdResponse::OK);

        ASSERT_EVENTS_SIZE(1);
        ASSERT_EVENTS_HLTH_CHECK_PING(0, Fw::Enabled::ENABLED, name);
    }
}

void HealthTester ::updatePingTimeout() {
    TEST_CASE(900.1.6, "Update ping timeouts");
    REQUIREMENT("ISF-HTH-006");
    COMMENT("The Svc::Health component shall have a command to update ping timeout values for a port");

    // Check initial state is as expected
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);
    ASSERT_CMD_RESPONSE_SIZE(0);

    for (FwIndexType entry = 0; entry < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; entry++) {
        this->clearEvents();
        this->clearHistory();
        // verify previous value
        ASSERT_EQ(this->pingEntries[entry].warnCycles, Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS + entry);
        ASSERT_EQ(this->pingEntries[entry].fatalCycles,
                  Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 2 + entry + 1);
        char name[80];
        snprintf(name, sizeof(name), "task%d", entry);
        Fw::CmdStringArg task(name);
        this->sendCmd_HLTH_CHNG_PING(0, 10, task, Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 4 + entry,
                                     Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 5 + entry);
        this->dispatchAll();
        // verify EVR
        ASSERT_EVENTS_SIZE(1);
        ASSERT_EVENTS_HLTH_PING_UPDATED_SIZE(1);
        ASSERT_EVENTS_HLTH_PING_UPDATED(0, name, Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 4 + entry,
                                        Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 5 + entry);
        ASSERT_EQ(this->component.m_pingTrackerEntries[entry].entry.warnCycles,
                  Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 4 + entry);
        ASSERT_EQ(this->component.m_pingTrackerEntries[entry].entry.fatalCycles,
                  Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 5 + entry);
    }
}

void HealthTester ::watchdogCheck() {
    TEST_CASE(900.1.7, "Watchdog check");
    REQUIREMENT("ISF-HTH-007");
    COMMENT(
        "The Svc::Health component shall stroke a watchdog port while all ping replies are within their limit and "
        "health checks pass");

    // Check initial state is as expected
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);
    ASSERT_CMD_RESPONSE_SIZE(0);

    // invoke run handler 10 times and check watchdog port output
    for (U32 i = 0; i < 10; i++) {
        this->invoke_to_Run(0, 0);
        ASSERT_from_WdogStroke_SIZE(i + 1);
        ASSERT_from_WdogStroke(i, this->component.m_watchDogCode);
        // increment watchdog code
        this->component.m_watchDogCode += 1;
    }
}

void HealthTester ::nominalCmd() {
    TEST_CASE(900.1.8, "Nominal Command");
    COMMENT("Process command during quiescent (no telemetry readout) period.");

    Fw::LogStringArg arg("task0");
    Fw::CmdStringArg carg(arg);

    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);
    ASSERT_CMD_RESPONSE_SIZE(0);

    sendCmd_HLTH_ENABLE(0, 0, Fw::Enabled::DISABLED);
    this->dispatchAll();
    ASSERT_EQ(Fw::Enabled(Fw::Enabled::DISABLED), this->component.m_enabled);
    ASSERT_EVENTS_SIZE(1);
    ASSERT_EVENTS_HLTH_CHECK_ENABLE(0, Fw::Enabled::DISABLED);
    this->clearEvents();

    sendCmd_HLTH_ENABLE(0, 0, Fw::Enabled::ENABLED);
    this->dispatchAll();
    ASSERT_EQ(Fw::Enabled(Fw::Enabled::ENABLED), this->component.m_enabled);
    ASSERT_EVENTS_SIZE(1);
    ASSERT_EVENTS_HLTH_CHECK_ENABLE(0, Fw::Enabled::ENABLED);

    this->clearEvents();
    sendCmd_HLTH_PING_ENABLE(0, 0, carg, Fw::Enabled::DISABLED);
    this->dispatchAll();
    ASSERT_EQ(Fw::Enabled(Fw::Enabled::DISABLED), this->component.m_pingTrackerEntries[0].enabled);
    ASSERT_EVENTS_SIZE(1);
    ASSERT_EVENTS_HLTH_CHECK_PING(0, Fw::Enabled::DISABLED, "task0");

    this->clearEvents();
    U32 warn_val = 9;
    U32 fatal_val = 99;
    sendCmd_HLTH_CHNG_PING(0, 0, carg, warn_val, fatal_val);
    this->dispatchAll();

    ASSERT_EQ(warn_val, this->component.m_pingTrackerEntries[0].entry.warnCycles);
    ASSERT_EQ(fatal_val, this->component.m_pingTrackerEntries[0].entry.fatalCycles);

    // Check no events or telemetry have occurred
    ASSERT_EVENTS_SIZE(1);
    ASSERT_EVENTS_HLTH_PING_UPDATED_SIZE(1);
    ASSERT_EVENTS_HLTH_PING_UPDATED(0, "task0", warn_val, fatal_val);
    ASSERT_TLM_SIZE(0);
}

void HealthTester ::nominal2CmdsDuringTlm() {
    TEST_CASE(900.1.9, "Nominal 2 commands called during telemetry readouts.");
    COMMENT("Process commands during busy (telemetry readout) period.");

    // Check no events or telemetry have occurred
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);

    for (FwIndexType entry = 0; entry < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; entry++) {
        this->keys[entry] = entry;
    }

    this->invoke_to_Run(0, 0);

    // Check no events or telemetry have occurred
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);

    sendCmd_HLTH_ENABLE(0, 0, Fw::Enabled::DISABLED);
    this->dispatchAll();
    ASSERT_EQ(Fw::Enabled(Fw::Enabled::DISABLED), this->component.m_enabled);

    return;
    // invoke schedIn handler for 50 times
    for (U32 i = 0; i < 50; i++) {
        printf("Invoke: %d\n", i);
        this->invoke_to_Run(0, 0);

        // Check no events or telemetry have occurred
        ASSERT_EVENTS_SIZE(0);
        ASSERT_TLM_SIZE(0);
    }

    sendCmd_HLTH_ENABLE(0, 0, Fw::Enabled::ENABLED);
    this->dispatchAll();
    ASSERT_EQ(Fw::Enabled(Fw::Enabled::ENABLED), this->component.m_enabled);

    this->invoke_to_Run(0, 0);

    // Check no events or telemetry have occurred
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);
}

void HealthTester ::miscellaneous() {
    TEST_CASE(900.1.10, "Miscellaneous remaining tests.");
    COMMENT("Case 1: Ping port anomalies.");

    // Check no events or telemetry have occurred
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);

    // send a bad ping return key
    this->override = true;
    this->override_key = FLAG_KEY_VALUE;

    // invoke schedIn
    this->invoke_to_Run(0, 0);
    this->dispatchAll();

    // check for expected EVRs
    ASSERT_EVENTS_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    ASSERT_EVENTS_HLTH_PING_WRONG_KEY_SIZE(Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS);
    for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS; port++) {
        char name[80];
        snprintf(name, sizeof(name), "task%d", port);
        ASSERT_EVENTS_HLTH_PING_WRONG_KEY(port, name, FLAG_KEY_VALUE);
    }

    this->clearEvents();
    this->clearTlm();

    COMMENT("Case 2: Command input anomalies.");

    // Check no events or telemetry have occurred
    ASSERT_EVENTS_SIZE(0);
    ASSERT_TLM_SIZE(0);

    // send command with bad value
    Fw::Enabled badValue = static_cast<Fw::Enabled::t>(Fw::Enabled::NUM_CONSTANTS);
    sendCmd_HLTH_PING_ENABLE(0, 0, Fw::CmdStringArg("task0"), badValue);
    this->dispatchAll();

    ASSERT_CMD_RESPONSE_SIZE(1);
    ASSERT_CMD_RESPONSE(0, HealthComponentBase::OPCODE_HLTH_PING_ENABLE, 0, Fw::CmdResponse::FORMAT_ERROR);

    // send command with bad ping entry
    sendCmd_HLTH_PING_ENABLE(0, 0, Fw::CmdStringArg("notask"), Fw::Enabled::ENABLED);
    this->dispatchAll();

    ASSERT_CMD_RESPONSE_SIZE(2);
    ASSERT_CMD_RESPONSE(1, HealthComponentBase::OPCODE_HLTH_PING_ENABLE, 0, Fw::CmdResponse::VALIDATION_ERROR);
    ASSERT_EVENTS_SIZE(1);
    ASSERT_EVENTS_HLTH_CHECK_LOOKUP_ERROR_SIZE(1);
    ASSERT_EVENTS_HLTH_CHECK_LOOKUP_ERROR(0, "notask");

    this->clearHistory();

    // send update timeout command with bad thresholds
    sendCmd_HLTH_CHNG_PING(0, 0, Fw::CmdStringArg("task0"), 10, 9);
    this->dispatchAll();

    ASSERT_CMD_RESPONSE_SIZE(1);
    ASSERT_CMD_RESPONSE(0, HealthComponentBase::OPCODE_HLTH_CHNG_PING, 0, Fw::CmdResponse::VALIDATION_ERROR);
    ASSERT_EVENTS_SIZE(1);
    ASSERT_EVENTS_HLTH_PING_INVALID_VALUES_SIZE(1);
    ASSERT_EVENTS_HLTH_PING_INVALID_VALUES(0, "task0", 10, 9);

    this->clearEvents();
    this->clearHistory();

    // send update timeout command with ping entry
    sendCmd_HLTH_CHNG_PING(0, 0, Fw::CmdStringArg("sometask"), 1, 2);
    this->dispatchAll();

    ASSERT_CMD_RESPONSE_SIZE(1);
    ASSERT_CMD_RESPONSE(0, HealthComponentBase::OPCODE_HLTH_CHNG_PING, 0, Fw::CmdResponse::VALIDATION_ERROR);

    this->clearEvents();
    this->clearHistory();

    COMMENT("Case 3: All ping ports except one respond to ping requests.");

    ASSERT_EVENTS_SIZE(0);

    // set up the correct keys for all ports except last
    for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS - 1; port++) {
        this->keys[port] = port;
    }
    this->keys[Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS - 1] = 9999;

    this->override = false;
    this->component.m_key = 0;

    // reset cycle counts
    for (FwIndexType i = 0; i < this->numPingEntries; i++) {
        this->component.m_pingTrackerEntries[i].cycleCount = 0;
    }

    // invoke schedIn handler
    for (U32 i = 0; i < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS * 2; i++) {
        // invoke schedIn
        this->invoke_to_Run(0, 0);

        for (FwIndexType port = 0; port < Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS - 1; port++) {
            if (i == 0) {
                this->keys[port] += Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS;
            } else {
                this->keys[port] += Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS - 1;
            }
        }
    }

    // check that only the last ping entry caused a warning EVR due to no ping response
    ASSERT_EVENTS_SIZE(1);
    ASSERT_EVENTS_HLTH_PING_WARN_SIZE(1);
    char name[80];
    snprintf(name, sizeof(name), "task%d", Svc::HealthComponentBase::NUM_PINGSEND_OUTPUT_PORTS - 1);
    ASSERT_EVENTS_HLTH_PING_WARN(0, name);
    ASSERT_TLM_SIZE(1);
    ASSERT_TLM_PingLateWarnings_SIZE(1);
    ASSERT_TLM_PingLateWarnings(0, 1);
}

void HealthTester::textLogIn(const FwEventIdType id,          //!< The event ID
                             const Fw::Time& timeTag,         //!< The time
                             const Fw::LogSeverity severity,  //!< The severity
                             const Fw::TextLogString& text    //!< The event string
) {
    TextLogEntry e = {id, timeTag, severity, text};

    printTextLogHistoryEntry(e, stdout);
}

}  // end namespace Svc
